home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / BCD.PART3.ARC / BCD PART III
Encoding:
Text File  |  2019-04-13  |  39.9 KB  |  890 lines

  1. ***********************************************************************
  2. This article is being presented through the *StarBoard* Journal of  the
  3. FlagShip/StarShip  SIGs  (Special  Interest Groups) on Delphi and GEnie
  4. telecommunication networks.  Permission is hereby granted to non-profit
  5. organizations only to reprint this article or pass it along electronic-
  6. ally as long as proper credit is given  to  both  the  author  and  the
  7. *StarBoard* Journal.  
  8. ***********************************************************************
  9.  
  10. THE ABC's OF BCD (part 3)
  11.  
  12. Manipulating Decimal Numbers In Machine Language
  13.  
  14. by W.J. Brier
  15. (TROUBLESOME on DELPHI)
  16. ===========================================================================
  17. INTRODUCTION
  18.  
  19. If you have thoroughly read THE ABC'S OF BCD parts 1 and 2, and have tried
  20. out the program examples that were presented, you should have an
  21. understanding of binary coded decimal (BCD) numbers and how to convert from
  22. ASCII to BCD and vice versa.  You should also have a fairly clear
  23. understanding of how to format your ASCII numbers to produce a pleasing and
  24. professional-appearing display.  If some of the concepts presented in part
  25. 2 are not clear, I suggest that you read over the material again and try
  26. out the program examples before continuing on with part 3 of this series.
  27.  
  28. In part 1 you were presented with the basic concept of binary coded decimal
  29. numbers and were shown how to store such numbers.  Routines were presented
  30. that illustrated the techniques of encoding a two digit ASCII number into a
  31. single BCD digit and decoding a single BCD digit back to ASCII numerals.
  32. Part 2 expanded on this knowledge by describing the techniques for encoding
  33. and decoding strings of digits, and formatting ASCII strings to suit the
  34. display needs of your program.
  35.  
  36. In THE ABC'S OF BCD part 3, methods of transferring BCD numbers from one
  37. place to another in memory will be discussed.  Programming techniques for
  38. performing addition and subtraction of BCD numbers will be described.
  39. Later, a complete evaluation routine will be presented that evaluates
  40. signed BCD numbers and performs the appropriate mathematical operations
  41. upon them.  Program examples will continue to use the same symbology used
  42. in parts 1 and 2 of the series.
  43.  
  44. I.  TRANSFERRING BCD NUMBERS BETWEEN MEMORY LOCATIONS
  45.  
  46. In all of the examples that have been presented in this series of articles,
  47. it was assumed that the BCD number to be operated upon was already in the
  48. BCD accumulator.  Naturally, this will not always be the case.  BCD numbers
  49. will be stored in various locations and will have to be transferred to one
  50. or both of the BCD accumulators to perform various operations.  The result
  51. left in BCD accumulator #1 (ACUM1) will then have to be sent to some other
  52. location in memory.  To accomplish these transfer operations some type of
  53. routine is needed to do the job.
  54.  
  55. If you may recall, it was recommended that all BCD numbers be composed of
  56. the same number of digits in your program.  Consistent length promotes
  57. easier conversion from ASCII to BCD and back and also makes it easy to
  58. transfer BCD numbers from one location to another.
  59.  
  60. Also, if all BCD numbers are the same length, a terminator is not required
  61. at the end of the BCD number.  With this in mind, let's see what it takes
  62. to move a BCD number from one location to another.
  63.  
  64. Transferring a BCD number simply requires that you set up some pointers to
  65. the address of the BCD number to be transferred (the SOURCE location) and
  66. some pointers to the address of the location to which it is to be
  67. transferred (the DESTINATION location).  Then, using a loop, the bytes are
  68. read one at a time from the source and stored at the destination location.
  69. In the example that follows, it is assumed that the BCD number is five
  70. bytes in length and that the zero page pointer locations are PTR1 and PTR2
  71. (each representing two bytes in zero page).  If the concept of zero page
  72. and post-indexed addressing is a bit fuzzy to you, a good text on 6502
  73. machine language programming will be of value.  Here is an algorithm that
  74. transfers a BCD number from the address contained in PTR1 and PTR1+1 to the
  75. address contained in PTR2 and PTR2+1:
  76.  
  77.     TRANS    LDY #$04       ;OFFSET & COUNTER
  78.                             ;
  79.     TRANS1   LDA (PTR1),Y   ;FETCH BYTE FROM SOURCE
  80.              STA (PTR2),Y   ;STORE AT DESTINATION
  81.                             ;
  82.              DEY            ;LOWER OFFSET/COUNTER
  83.              BPL TRANS1     ;IF NOT NEGATIVE THEN LOOP
  84.                             ;
  85.              RTS            ;OR ELSE EXIT
  86.  
  87. This routine simply loops backwards, moving one byte at a time and ending
  88. when the .Y register WRAPS AROUND to $FF.  Notice that the .Y register is
  89. set to $04 (not $05) at the beginning of the routine.  Other length BCD
  90. numbers could be transferred with this routine by loading .Y with an appro-
  91. priate value.
  92.  
  93. To use this subroutine, it is necessary to store the source and destination
  94. addresses in zero page pointers PTR1 and PTR2 respectively.  Here is an
  95. example of how to transfer a number from location SRC to BCD accumulator
  96. ACUM1:
  97.  
  98.              LDX #<SRC      ;SOURCE ADDRESS LOW BYTE
  99.              LDY #>SRC      ;SOURCE ADDRESS HIGH BYTE
  100.              STX PTR1
  101.              STY PTR1+1
  102.                             ;
  103.              LDX #<ACUM1    ;ACUM1 ADDRESS LOW BYTE
  104.              LDY #>ACUM1    ;ACUM1 ADDRESS HIGH BYTE
  105.              STX PTR2
  106.              STY PTR2+1
  107.                             ;
  108.              JSR TRANS      ;CALL TRANSFER ROUTINE
  109.  
  110.              program continues...
  111.  
  112. Upon exiting from this program fragment ACUM1 will contain the same number
  113. as the source location SRC.  SRC is undisturbed and the .Y register will
  114. contain $FF.  The .X register is undisturbed as it is not used.
  115.  
  116. The need to transfer BCD numbers between memory and the BCD accumulators
  117. will take on considerable importance when it becomes necessary to perform
  118. mathematical operations such as addition and subtraction.  All mathematical
  119. operations will be initiated by storing one BCD number in ACUM1 and the
  120. other in ACUM2.
  121.  
  122. II.  ELEMENTARY MATHEMATICS IN MACHINE LANGUAGE
  123.  
  124. Before this discussion takes up the subject of adding and subtracting BCD
  125. numbers, a brief review of machine language mathematics will be presented.
  126. This should facilitate easier understanding of multi-byte BCD evaluation.
  127.  
  128. The 65XX/85XX microprocessor has instructions for performing single byte
  129. addition or subtraction.  These instructions, as you may know, are ADC (add
  130. with carry) and SBC (subtract with borrow).  There are also two
  131. instructions which determine whether these operations are to be performed
  132. in binary or decimal mode.  They are SED (set decimal mode) and CLD (clear
  133. decimal mode).  Almost all programs operate in binary mode and thus usually
  134. perform a CLD (which is also performed during the power-up process when you
  135. first turn on the computer).
  136.  
  137. The effect of the CLD instruction is to cause a carry in addition when the
  138. result in the accumulator (.A register) exceeds $FF, whereas an SED
  139. instruction will cause a carry to occur if the value in .A exceeds $99.  In
  140. both modes, a borrow during subtraction occurs if the value in .A is
  141. reduced to less than zero.  However, several important effects occur in
  142. decimal mode which will now be discussed.
  143.  
  144. To more clearly understand exactly what the CPU does when adding in decimal
  145. mode here is an example of adding two bytes of small value, one example in
  146. binary mode and one in decimal mode:
  147.  
  148.      BINARY                 DECIMAL
  149.     ===============================
  150.       $06                     $06
  151.     + $07                   + $07
  152.     ===============================
  153.       $0D                     $13
  154.  
  155. Notice that the sum on the left is the binary result of adding the two
  156. numbers.  Six plus seven does indeed equal thirteen.  The sum on the right
  157. reflects the effect of operating the CPU in decimal mode.  An internal
  158. half-carry has been generated because the units column, when added, exceeds
  159. decimal nine.  The CPU has performed BCD addition, and did not require any
  160. extra programming to take care of the carry from the units to the tens.
  161.  
  162. Here is another example of the difference between binary and decimal mode
  163. addition:
  164.  
  165.      BINARY                 DECIMAL
  166.     ===============================
  167.       $16                     $16
  168.     + $15                   + $15
  169.     ===============================
  170.       $2B                     $31
  171.  
  172. The hex value $16 is equal to 22 in decimal and of course $15 is 21 in
  173. decimal.  The two result in the sum of 43, which is $2B in hex.  Addition
  174. has been performed in binary.  Note, however, that the decimal example
  175. shows the effect of the half-carry from the units column to the tens
  176. column.  Again, BCD addition has been performed.
  177.  
  178. As with addition, running the CPU in decimal mode results in different
  179. behavior in subtraction than in binary mode.  Again, here is an example of
  180. the difference:
  181.  
  182.      BINARY                 DECIMAL
  183.     ===============================
  184.       $16                     $16
  185.     - $07                   - $07
  186.     ===============================
  187.       $0F                     $09
  188.  
  189. If you figure out the result of the binary operation on the left you will
  190. see that $16 (22) minus $07 (7) does equal $0F (15).  The decimal version
  191. demonstrates another feature of decimal mode.  The CPU automatically
  192. generates an internal half-borrow to account for the subtraction of the
  193. units column.  The result is BCD subtraction.
  194.  
  195. None of the above examples take the carry bit in the CPU status register
  196. into account.  But, ADC really means (AD)d with (C)arry and SBC signifies
  197. (S)u(B)tract with (C)arry.  Therefore, the state of the carry bit must be
  198. known prior to commencing with either of these mathematical operations.  If
  199. a multi-precision number (more than one byte) is to be added to another for
  200. example, the carry will be used to handle any overflow that may occur while
  201. addition is in progress.  Similarly, in subtracting multi-precision
  202. numbers, the carry bit will act as an INVERTED BORROW, and will account for
  203. underflow while subtraction is in progress.  Therefore, the carry bit must
  204. be cleared (CLC) prior to any addition and or set (SEC) prior to any
  205. subtraction.
  206.  
  207. In adding or subtracting multi-precision numbers, the carry behaves
  208. differently in decimal mode than in binary mode.  This is best illustrated
  209. with an example of adding two multi-precision numbers together in the two
  210. modes.  It is assumed that the carry bit was cleared with a CLC instruction
  211. prior to the actual addition operation:
  212.  
  213.       BINARY                DECIMAL
  214.     ===============================
  215.       $04 $86               $04 $86
  216.     + $03 $77             + $03 $77
  217.     ===============================
  218.       $07 $FD               $08 $63
  219.  
  220. In the binary example addition of the right hand column did not generate a
  221. carry as the sum of $86 and $77 did not exceed the $FF limit of the
  222. accumulator.  Thus the sum of the left hand column was unaffected by the
  223. addition of the right hand column.  In the decimal operation, note that a
  224. carry did occur to the left hand column, and that a half-carry occurred in
  225. the right hand column.  The carry to the left resulted because the value in
  226. .A exceeded $99.  In decimal mode such an event sets the carry bit, causing
  227. it to be added into the next operation.  Therefore, the left hand column is
  228. really $04 + $03 + $01 (the carry bit) resulting in $08.  The result is
  229. multi-precision BCD addition.
  230.  
  231. A similar effect can be seen in multi-precision BCD subtraction.  Again, an
  232. example will be presented to illustrate the difference between binary and
  233. decimal mode.  It is assumed that the carry bit has been set with an SEC
  234. instruction prior to subtraction:
  235.  
  236.       BINARY                DECIMAL
  237.     ===============================
  238.       $04 $77               $04 $77
  239.     - $03 $88             - $03 $88
  240.     ===============================
  241.       $00 $EF               $00 $89
  242.  
  243. In the binary mode operation, subtraction of the right hand column resulted
  244. in a borrow from the left column, as was also the case in the decimal mode
  245. operation.  Note however, that a half-borrow had to be performed in the
  246. decimal mode operation, as subtracting the units in the right hand column
  247. would have resulted in a negative value.  No such half-borrow is ever
  248. performed in binary mode.
  249.  
  250. To summarize the effect upon the carry bit in addition and subtraction, the
  251. carry is set in binary mode if the result of addition exceeds $FF or 255.
  252. In decimal mode the carry is set if addition causes the result to exceed
  253. $99.  Upon exceeding $99, the .A register wraps around to $00, whereas this
  254. wraparound in binary mode does not occur until the result exceeds $FF.
  255.  
  256. Subtraction clears the carry bit if the result is less than zero in either
  257. case.  However, going below zero in binary mode wraps the accumulator
  258. around to $FF while in decimal mode it wraps around to $99.  Also, in
  259. decimal mode the CPU automatically takes care of the half-carry or
  260. half-borrow between the units and tens columns when adding or subtracting.
  261. This half-carry or half-borrow never occurs in binary mode.
  262.  
  263. Understanding the essentials of addition and subtraction naturally leads
  264. into addition of BCD numbers, the subject of the next chapter.
  265.  
  266. III.  ADDING MULTI-PRECISION BCD NUMBERS
  267.  
  268. Addition of multi-precision BCD numbers is accomplished in the same manner
  269. as in adding binary numbers, the difference being the manner in which the
  270. carry is handled.  Before going to multi-precision addition, let's review
  271. the procedure for adding two bytes.
  272.  
  273. To add two bytes the .A register is loaded with one of the bytes and an add
  274. with carry (ADC) operation is performed using the other byte as an operand.
  275. The result is deposited in the .A register.  To avoid having the carry bit
  276. included in the sum a CLC instruction must be issued prior to actually
  277. performing addition.  So, to add NUM1 to NUM2 you would code the following:
  278.  
  279.     BINARY                  DECIMAL
  280.     ================================
  281.                             SED
  282.     CLC                     CLC
  283.     LDA NUM1                LDA NUM1
  284.     ADC NUM2                ADC NUM2
  285.                             CLD
  286.     ================================
  287.  
  288. Exiting from this routine results in the sum of NUM1 and NUM2 being left in
  289. the .A register.  The most obvious difference between the two techniques is
  290. the use of the SED (set decimal mode) and CLD (clear decimal mode)
  291. instructions in the decimal version.  Otherwise, the two examples operate
  292. in exactly the same fashion.  As described above, the SED instruction will
  293. cause an internal half-carry to be generated if the units column exceeds
  294. nine when added.
  295.  
  296. To perform multi-precision BCD addition the routine illustrated above must
  297. be incorporated into a loop.  The loop starts with the least significant
  298. digits and successively adds columns to produce the sum for the two
  299. numbers.  The carry is cleared only prior to entering the loop.  Then as
  300. the loop progresses, any carry that is generated is added in the next
  301. column, resulting in correct addition.  This technique functions on the
  302. assumption that both BCD numbers are the same length (five bytes in this
  303. case).
  304.  
  305. Assuming that one BCD number is stored in ACUM1 and the other in ACUM2,
  306. here is a routine to add the two numbers together and store the sum in
  307. ACUM1:
  308.  
  309.     ADD      SED            ;SET DECIMAL MODE
  310.              CLC            ;CLEAR CARRY BIT
  311.              LDY #$04       ;OFFSET & COUNTER
  312.                             ;
  313.     ADD1     LDA ACUM1,Y    ;FETCH BCD DIGIT
  314.              ADC ACUM2,Y    ;ADD
  315.              STA ACUM1,Y    ;SAVE RESULT
  316.                             ;
  317.              DEY            ;NEXT DIGIT
  318.              BPL ADD1       ;NOT DONE, SO LOOP
  319.                             ;
  320.              CLD            ;RETURN TO BINARY MODE
  321.                             ;
  322.              RTS            ;AND EXIT
  323.  
  324. Upon exiting from this routine, ACUM1 will contain the sum of the two BCD
  325. numbers, ACUM2 will be undisturbed, the .A register will contain the most
  326. significant BCD digit that resulted from the addition, the .Y register will
  327. contain $FF and the .X register will be undisturbed.  A total of five
  328. iterations will be performed.
  329.  
  330. To better understand just how this routine actually adds two BCD numbers,
  331. here is is an example of what ACUM1 would look like at each loop through
  332. the routine (values are hex numbers and ACUM2 is not shown for each loop as
  333. it doesn't change):
  334.  
  335.     LOOP     ACUM1            ACUM2           CARRY
  336.     ===============================================
  337.     START    18 57 21 00 91   46 57 00 82 10    0
  338.  
  339.      1       18 57 21 00 01                     1
  340.      2       18 57 21 83 01                     0
  341.      3       18 57 21 83 01                     0
  342.      4       18 14 21 83 01                     1
  343.      5       65 14 21 83 01                     0
  344.     ===============================================
  345.  
  346. Try coding the above routine and entering these numbers to see for yourself
  347. that:
  348.  
  349.       $18 $57 $21 $00 $91
  350.     + $46 $57 $00 $82 $10
  351.     =====================
  352.       $65 $14 $21 $83 $01
  353.  
  354. is indeed the result of BCD addition.  To see the effect of not having the
  355. CPU in decimal mode, replace the SED instruction with a NOP instruction and
  356. run the routine again with the same numbers.  Needless to say, the result
  357. will be very different.
  358.  
  359. While the subroutine presented above does correctly add two BCD numbers it
  360. does not check for possible overflow of either the most significant digit
  361. or the BCD accumulator itself.  Consider the following operation:
  362.  
  363.       41 00 00 00 00
  364.     + 40 00 00 00 00
  365.     ================
  366.       81 00 00 00 00
  367.  
  368. What's wrong with it, you say?  The result is actually a negative number
  369. because the sign bit has been set.  The most significant digit has been
  370. overflowed.  When the number is decoded into ASCII the result will be
  371. -100000000, not 8100000000 as you might expect.  Clearly, some means of
  372. avoiding an overflow into the sign bit must be devised.
  373.  
  374. It is also possible to overflow the entire BCD accumulator.  If the
  375. following operation is performed:
  376.  
  377.       50 00 00 00 00
  378.     + 60 00 00 00 00
  379.  
  380. the BCD accumulator will contain:
  381.  
  382.     10 00 00 00 00
  383.  
  384. and the carry (C) flag will be set.  The correct value should be:
  385.  
  386.     01 10 00 00 00 00
  387.  
  388. However, there isn't a sixth byte in the BCD accumulator.  The result is an
  389. erroneous value.  Therefore, in addition to avoiding an overflow into the
  390. sign bit an overflow of the accumulator must also be prevented.
  391.  
  392. Detecting an accumulator overflow is quite easy as the C flag will be set
  393. if such an overflow did occur.  Indentifying an overflow into the sign bit,
  394. at first glance, doesn't seem to be all that easy to accomplish, as the C
  395. flag will be cleared upon leaving the ADD subroutine.  Fortunately, the
  396. overflow (V) flag in the status register is tailor-made for this purpose.
  397. The V flag is set to one any time a mathematical or logical operation on
  398. the .A register results in a binary value in excess of 0111 1111 or $7F.
  399.  
  400. For example, the following binary operation:
  401.  
  402.       0111 1111 ($7F)
  403.     + 0000 0001 ($01)
  404.     =================
  405.       1000 0000 ($80)
  406.  
  407. will cause the V flag to be set.  This is because the addition of the two
  408. numbers generated a binary carry from the sixth to the seventh bit.  Thus,
  409. the sixth bit has been overflowed into the seventh bit.
  410.  
  411. When calling the ADD routine the last addition operation performed is upon
  412. the most significant digit, which happens to contain the sign bit.  If the
  413. result of that operation overflows the sign bit, the V flag will be set
  414. upon exit from the routine.  If no overflow occurred, the V flag will be
  415. cleared.  Therefore, testing the V flag is all that is required to detect
  416. the sign bit overflow.  Coupled with a C flag test for accumulator
  417. overflow, execution of the program can be re-directed to an error routine
  418. that advises the user of the overflow.  Coding would take this form:
  419.  
  420.              JSR ADD        ;ACUM1 = ACUM1 + ACUM2
  421.              BCC NEXT       ;NO ACCUMULATOR OVERFLOW
  422.                             ;
  423.     OVRFLO   JMP ERROR      ;INFORM USER OF OVERFLOW
  424.                             ;
  425.     NEXT     BVS OVRFLO     ;SIGN BIT OVERFLOW
  426.  
  427.              program continues...
  428.  
  429. The ERROR routine would display a suitable error message to the user and
  430. would halt further number processing.  Assuming that neither type of error
  431. occurred then the program would continue to the next step.
  432.  
  433. To briefly review, addition of BCD numbers is performed by placing one
  434. number into BCD accumulator ACUM1 and the other into ACUM2.  A subroutine
  435. is called which iteratively adds the digits of the two numbers.  Upon exit
  436. from the addition subroutine, the C and V flags are tested for a possible
  437. overflow.
  438.  
  439. IV.  SUBTRACTING MULTI-PRECISION BCD NUMBERS
  440.  
  441. As with multi-precision addition, subtraction of multiprecision BCD numbers
  442. is accomplished in the same manner as in subtracting binary numbers, again
  443. the difference being the manner in which the carry is handled.  Before
  444. going to multi-precision subtraction, let's review the procedure for
  445. subtracting two bytes.
  446.  
  447. To subtract two bytes the .A register is loaded with one of the bytes (the
  448. minuend) and a subtract with carry (SBC) operation is performed using the
  449. other byte (the subtrahend) as an operand.  The difference is deposited in
  450. the .A register.  To avoid having the carry bit influence the difference a
  451. set carry (SEC) instruction must be issued prior to actually performing
  452. subtraction.  In subtraction the carry functions as an inverted borrow.
  453.  
  454. So, to subtract NUM1 to NUM2 you would code the following:
  455.  
  456.     BINARY                  DECIMAL
  457.     ================================
  458.                             SED
  459.     SEC                     SEC
  460.     LDA NUM1                LDA NUM1
  461.     SBC NUM2                SBC NUM2
  462.                             CLD
  463.     ================================
  464.  
  465. Exiting from this routine results in the difference of NUM1 and NUM2 being
  466. left in the .A register.  Again, the difference between the two techniques
  467. is the use of the SED and CLD instructions in the decimal version.
  468. Otherwise, the two examples operate in exactly the same fashion.  The SED
  469. instruction will cause an internal half-borrow to be generated if the units
  470. column goes below zero when subtracted.
  471.  
  472. To perform multi-precision BCD subtraction the routine illustrated above
  473. must be incorporated into a loop.  The loop starts with the least
  474. significant digits and successively subtracts columns to produce the
  475. difference for the two numbers.  The carry is set only prior to entering
  476. the loop.  As with the addition subroutine, this technique functions on the
  477. assumption that both BCD numbers are the same length (five bytes in this
  478. case).
  479.  
  480. Assuming that the minuend has been stored in ACUM1 and the subtrahend in
  481. ACUM2, here is a routine to subtract the two numbers and store the
  482. difference in ACUM1:
  483.  
  484.     SUB      SED            ;SET DECIMAL MODE
  485.              SEC            ;SET CARRY BIT
  486.              LDY #$04       ;OFFSET & COUNTER
  487.                             ;
  488.     SUB1     LDA ACUM1,Y    ;FETCH MINUEND
  489.              SBC ACUM2,Y    ;SUBTRACT SUBTRAHEND
  490.              STA ACUM1,Y    ;SAVE DIFFERENCE
  491.                             ;
  492.              DEY            ;NEXT DIGIT
  493.              BPL SUB1       ;NOT DONE, SO LOOP
  494.                             ;
  495.              CLD            ;RETURN TO BINARY MODE
  496.                             ;
  497.              RTS            ;AND EXIT
  498.  
  499. Upon exiting from this routine, ACUM1 will contain the difference of the
  500. two BCD numbers, ACUM2 will be undisturbed, the .A register will contain
  501. the most significant BCD digit that resulted from the subtraction, the .Y
  502. register will contain $FF and the .X register will be undisturbed.  A total
  503. of five iterations will be performed.
  504.  
  505. To better understand just how this routine actually subtracts two BCD
  506. numbers, here is is an example of what ACUM1 would look like at each loop
  507. through the routine (values are hex numbers and ACUM2 is not shown for each
  508. loop as it doesn't change):
  509.  
  510.     LOOP     ACUM1            ACUM2           CARRY
  511.     ===============================================
  512.     START    65 14 21 83 01   46 57 00 82 10    1
  513.  
  514.      1       65 14 21 83 91                     0
  515.      2       65 14 21 00 91                     1
  516.      3       65 14 21 00 91                     1
  517.      4       65 57 21 00 91                     0
  518.      5       18 57 21 00 91                     1
  519.     ===============================================
  520.  
  521. Try coding the above routine and entering these numbers to see for yourself
  522. that:
  523.  
  524.       $65 $14 $21 $83 $01
  525.     - $46 $57 $00 $82 $10
  526.     =====================
  527.       $18 $57 $21 $00 $91
  528.  
  529. is indeed the result of BCD subtraction.  To see the effect of not having
  530. the CPU in decimal mode, replace the SED instruction with a NOP instruction
  531. and run the routine again with the same numbers.  Needless to say, the
  532. result will be very different.
  533.  
  534. Although the above routine does subtract two BCD numbers, it doesn't
  535. account for the possibility of a negative difference, which would be the
  536. result of the subtrahend being greater than the minuend.  Attempting to
  537. subtract:
  538.  
  539.       $49 $99 $99 $99 $99
  540.     - $50 $00 $00 $00 $00
  541.  
  542. should result in:
  543.  
  544.     - $00 $00 $00 $00 $01
  545.  
  546. which in the system of signed numbers being used would be:
  547.  
  548.     $80 $00 $00 $00 $01
  549.  
  550. the set seventh bit of the most significant digit indicating that this is a
  551. negative number.  However, the actual result left in ACUM1 will be:
  552.  
  553.     $99 $99 $99 $99 $99
  554.  
  555. This result is called the NINES COMPLEMENT of the difference.  The nines
  556. complement occurs due to the fact that when in decimal mode the .A register
  557. wraps around to $99 when the value it contains goes below zero.  Here is a
  558. table of nines complements and the actual values that they represent (all
  559. values are in hex):
  560.  
  561.     NINES                 ACTUAL
  562.     ============================
  563.      99                     01
  564.      98                     02
  565.      97                     03
  566.     ----                   ----
  567.      50                     50
  568.     ----                   ----
  569.      03                     97
  570.      02                     98
  571.      01                     99
  572.      00                     00
  573.     ============================
  574.  
  575. As can be seen from examining the table there is a definite relationship
  576. between the actual value and its nines complement.  One can deduce from
  577. this relationship that if the nines complement value is subtracted from
  578. zero (with the CPU in decimal mode) the result will be the actual value.
  579. This could be considered taking the nines complement of the nines
  580. complement.  The resulting inversion produces a proper decimal value.
  581.  
  582. The subtraction routine given above must be modified to properly handle
  583. nines complement results by performing the additional subtraction required
  584. to change the nines complement back to a true decimal value.  However, it
  585. is necessary to determine when the result is actually a nines complement
  586. and when it isn't.  Fortunately, the carry (C) flag will indicate if the
  587. last subtraction resulted in a negative value.  If upon leaving the
  588. subtraction loop, the C flag is set, the result is positive and no further
  589. action is required.  If the C flag is cleared, then a borrow occurred on
  590. the last subtraction and a nines complement number has been left in ACUM1.
  591. The inversion of the ACUM1 value must be performed.
  592.  
  593. Here is the modified routine:
  594.  
  595.     SUB      SED            ;DECIMAL MODE
  596.              SEC            ;SET CARRY FOR SUBTRACTION
  597.              LDY #$04       ;OFFSET & COUNTER
  598.                             ;
  599.     SUB1     LDA ACUM1,Y    ;FETCH MINUEND
  600.              SBC ACUM2,Y    ;SUBTRACT SUBTRAHEND
  601.              STA ACUM1,Y    ;STORE DIFFERENCE
  602.                             ;
  603.              DEY            ;LOWER OFFSET
  604.              BPL SUB1       ;NEXT ITERATION
  605.                             ;
  606.              BCS SUB3       ;RESULT WAS POSITIVE
  607.                             ;
  608.                             ;RESULT IS NINES COMPLEMENT
  609.                             ;
  610.              SEC            ;SET CARRY FOR SUBTRACTION
  611.              LDY #$04       ;OFFSET & COUNTER
  612.                             ;
  613.     SUB2     LDA #$00
  614.              SBC ACUM1,Y    ;INVERT NINES COMPLEMENT
  615.              STA ACUM1,Y    ;STORE DIFFERENCE
  616.                             ;
  617.              DEY            ;LOWER OFFSET
  618.              BPL SUB2       ;NEXT ITERATION
  619.                             ;
  620.              ORA #$80       ;SET SIGN BIT...
  621.              STA ACUM1      ;TO SHOW NEGATIVE NUMBER
  622.                             ;
  623.     SUB3     CLD            ;BINARY MODE
  624.                             ;
  625.              RTS            ;EXIT
  626.  
  627. The first portion of the routine is the same subtraction algorithm that was
  628. presented earlier.  However, upon completion of the last subtraction
  629. operation, the carry (C) flag is tested for a possible negative result.  If
  630. the carry is set, the routine branches to the end where the decimal flag is
  631. cleared.  If the C flag is cleared then a borrow occurred on the last
  632. subtraction operation and the result is a nines complement number.
  633.  
  634. To invert the nines complement number, subtraction is performed once again,
  635. with the nines complement value now the subtrahend and zero as the minuend.
  636. The effect is to invert the nines complement value resulting in the actual
  637. difference.  Lastly, the sign bit is set to indicate that the difference is
  638. negative.
  639.  
  640. Looking back in review, subtraction of multi-precision BCD numbers is
  641. performed by placing the minuend into BCD accumulator #1 (ACUM1) and
  642. placing the subtrahend into ACUM2.  The carry and decimal flags are set and
  643. ACUM2 is iteratively subtracted from ACUM1.  At the end of the subtraction
  644. the C flag is tested for a borrow.
  645.  
  646. If the flag is set, the difference is positive.  If the flag is cleared the
  647. difference is a nines complement value and must be inverted.  Following
  648. inversion, the sign bit is set in ACUM1 to indicate that the result is a
  649. negative number.
  650.  
  651. V.  SIGNED BCD NUMBER EVALUATION
  652.  
  653. The mathematical routines that have been presented up to this point perform
  654. either addition or subtraction.  They operate only on unsigned numbers
  655. (although the subtraction routine will generate a signed result if
  656. necessary).  Obviously, some type of evaluation routine is needed that can
  657. determine from the signs of the numbers what operations have to be invoked.
  658. This routine would have to determine the signs of the BCD accumulators and
  659. then, based on the signs, select the correct operation (addition or
  660. subtraction).  Following the actual math operation, the evaluation would
  661. have to conclude by restoring the correct sign to the result.  A special
  662. case would be required when subtraction results in a difference of zero.
  663.  
  664. The initial action in the evaluation routine would be to clear the sign
  665. flags SFLG1 and SFLG2 to zero.  This would indicate that the BCD
  666. accumulators both contain positive numbers (in this evaluation zero is
  667. considered positive).  Next, the first byte of ACUM1 would be tested for
  668. negativity and if found to be negative, SFLG1 would be set to indicate this
  669. condition.  Because the addition and subtraction routines operate only on
  670. unsigned numbers, it would also be necessary to mask the sign bit of ACUM1.
  671. The same evaluation would then be performed on ACUM2 with the sign bit
  672. being masked and SFLG2 set if ACUM2 is negative.  Here is the sign checking
  673. portion of the evaluation routine:
  674.  
  675.     EVAL     LDA #$00
  676.              STA SFLG1      ;CLEAR SIGN FLAGS
  677.              STA SFLG2
  678.                             ;
  679.              LDA ACUM1      ;CHECK SIGN
  680.              BPL EVAL1      ;POSITIVE NUMBER
  681.                             ;
  682.              AND #%01111111 ;MASK SIGN BIT w/$7F
  683.              STA ACUM1
  684.                             ;
  685.              DEC SFLG1      ;SET SIGN FLAG TO NEGATIVE
  686.                             ;
  687.     EVAL1    LDA ACUM2      ;CHECK SIGN
  688.              BPL EVAL2      ;POSITIVE NUMBER
  689.                             ;
  690.              AND #%01111111 ;MASK SIGN BIT
  691.              STA ACUM2
  692.                             ;
  693.              DEC SFLG2      ;SET SIGN FLAG
  694.                             ;
  695.     EVAL2    program continues...
  696.  
  697. Upon reaching EVAL2 in this program fragment each sign flag will be set or
  698. cleared according to the sign of the number in the associated BCD
  699. accumulator, and the sign bit will be cleared if the number is negative.
  700.  
  701. The function of ANDing the first byte of each accumulator is to dispose of
  702. the sign bit.  For example, this is how the operation would affect the
  703. first byte of the BCD number in ACUM1 if it was negative:
  704.  
  705.     ACUM1 %1000 0111 = $87
  706.     AND   %0111 1111 = $7F
  707.     ======================
  708.     ACUM1 %0000 0111 = $07
  709.  
  710. As can be seen, the sign bit has been cleared and the byte contains only a
  711. valid BCD digit.  This is stored back into the BCD accumulator.
  712.  
  713. With the sign evaluation completed, the next step would be to determine
  714. which math operation (addition or subtraction) must be performed.  Some
  715. generalizations can be made at this point.  If the signs of the two numbers
  716. are the same (both positive or both negative) the numbers must be added.
  717. If the signs are opposite, they must be subtracted.  If subtraction is
  718. performed, the sign of the difference must be inverted if the difference is
  719. not zero and if the minuend is negative.  Perhaps the best way to visual-
  720. ize these relationships is to construct a TRUTH TABLE of the various
  721. permutations that can result.  In the table, ACUM1 will be referred to as
  722. A1 and ACUM2 will be A2:
  723.  
  724.     A1    A2    OPER. ==> A1
  725.     ========================
  726.     +     +     ADD       +
  727.     +     -     SUB       *
  728.     -     -     ADD       -
  729.     -     +     SUB       *
  730.     =======================
  731.  
  732. The asterisks following the SUB operation indicate that the sign depends on
  733. whether the absolute (unsigned) value of the minuend was larger or smaller
  734. than that of the subtrahend.  To make a determination of such a case
  735. another truth table is needed:
  736.  
  737.     EXPRESSION   SFLG1    RESULT
  738.     ============================
  739.      A1 > A2      +         +
  740.      A1 > A2      -         -
  741.      A1 < A2      +         -
  742.      A1 < A2      -         +
  743.      A1 = A2      *         0
  744.     ============================
  745.  
  746. It is apparent from studying this table that if the sign of ACUM1 is
  747. positive, the sign of the difference from subtraction will be correct.  If
  748. ACUM1 is negative then the sign of the difference will be the opposite of
  749. what it should be and therefore must be inverted by logical operations.  If
  750. the difference is zero the sign will always be positive.  Thus a check for
  751. a zero difference must be made after subtraction has been completed.  This
  752. could be structured into a subroutine for use by any other part of your
  753. program that needs to detect whether the BCD number in ACUM1 is zero.  Here
  754. is a zero checking subroutine:
  755.  
  756.     ZCHK     CLC            ;CARRY IS ZERO FLAG
  757.              LDY #$04       ;OFFSET
  758.                             ;
  759.     ZCHK1    LDA ACUM1,Y    ;FETCH DIGIT
  760.              BNE ZCHKO      ;BCD NUMBER NOT ZERO
  761.                             ;
  762.              DEY
  763.              BPL ZCHK1      ;LOOP
  764.                             ;
  765.              SEC            ;INDICATE ZERO
  766.                             ;
  767.     ZCHKO    RTS            ;EXIT
  768.  
  769. Upon exit from this routine the carry flag will be set if the BCD number in
  770. ACUM1 equals zero.  As usual, the initial value for the .Y register assumes
  771. a BCD number length of five digits.
  772.  
  773. One other operation that the evaluation subroutine must also perform is
  774. trapping an overflow of ACUM1 or of the sign bit.  Here is the complete
  775. evaluation subroutine:
  776.  
  777.     EVAL     LDX #$00
  778.              STX SFLG1      ;CLEAR SIGN FLAGS
  779.              STX SFLG2
  780.                             ;
  781.              LDA ACUM1      ;CHECK SIGN
  782.              BPL EVAL1      ;POSITIVE NUMBER
  783.                             ;
  784.              AND #$7F       ;MASK SIGN BIT
  785.              STA ACUM1
  786.                             ;
  787.              DEC SFLG1      ;SET SIGN FLAG TO NEGATIVE
  788.                             ;
  789.     EVAL1    LDA ACUM2      ;CHECK SIGN
  790.              BPL EVAL2      ;POSITIVE NUMBER
  791.                             ;
  792.              AND #$7F       ;MASK SIGN BIT
  793.              STA ACUM2
  794.                             ;
  795.              DEC SFLG2      ;SET SIGN FLAG
  796.                             ;
  797.     EVAL2    LDA SFLG1
  798.              CMP SFLG2      ;COMPARE SIGNS
  799.              BNE EVAL4      ;OPPOSITE, SO SUBTRACT
  800.                             ;
  801.              JSR ADD        ;ADD
  802.              BCS EVAL3      ;OVERFLOW
  803.                             ;
  804.              BVC EVAL5      ;NO SIGN BIT OVERFLOW
  805.                             ;
  806.              SEC            ;SIGN BIT OVERFLOW
  807.                             ;
  808.     EVAL3    RTS            ;ABORT DUE TO OVERFLOW
  809.                             ;
  810.     EVAL4    JSR SUB        ;SUBTRACT
  811.                             ;
  812.              BIT ACUM1      ;CHECK DIFFERENCE SIGN
  813.              BMI EVAL5      ;NEGATIVE DIFFERENCE
  814.                             ;
  815.              JSR ZCHKO      ;CHECK FOR ZERO RESULT
  816.              BCS EVALO      ;ZERO, NO SIGN CHANGE
  817.                             ;
  818.     EVAL5    BIT SFLG1      ;CHECK SIGN
  819.              BPL EVALO      ;NO SIGN CHANGE
  820.                             ;
  821.              LDA ACUM1      ;
  822.              EOR #%10000000 ;INVERT SIGN BIT
  823.              STA ACUM1      ;SAVE
  824.                             ;
  825.     EVALO    CLC            ;NO OVERFLOW ERROR
  826.                             ;
  827.              RTS            ;EXIT
  828.  
  829. As already discussed, the first portion of the routine checks the signs of
  830. the two BCD numbers and performs appropriate operations if one or both
  831. signs are negative.  At EVAL2 the sign flags are checked for equality.
  832.  
  833. As pointed out before, if the signs are the same, the numbers must be
  834. added.  This is accomplished by calling the ADD subroutine (described in
  835. chapter III).  Otherwise, the numbers are subtracted by calling the SUB
  836. subroutine (described in chapter IV).
  837.  
  838. Following a call to ADD the evaluation will abort with the carry (C) flag
  839. set if an overflow occurred during addition (the set C flag indicates an
  840. overflow error to the portion of the program that called EVAL).  If no
  841. overflow occurred the routine will branch to EVAL5 where the original sign
  842. of ACUM1 will be tested.  If that sign was negative the sign bit of ACUM1
  843. will be set to indicate negativity.  This is accomplished by TOGGLING bit
  844. seven of the most significant byte of ACUM1 (using the EOR instruction).
  845. The effect of this is to set a cleared bit or clear a set bit.  From the
  846. standpoint of addition, the toggling effect isn't important because addi-
  847. tion always results in an unsigned number.  Thus, the only effect of the
  848. EOR (exclusive-or) instruction is to set the sign bit.
  849.  
  850. Upon leaving the SUB subtraction subroutine, EVAL calls ZCHK to check for a
  851. zero result (unless the sign of ACUM1 is negative).  If the result was zero
  852. the sign check is skipped and the routine ends.  Otherwise execution
  853. continues through to the sign check at EVAL5.  Here is where the toggling
  854. effect of the exclusive-or function becomes important.  According to the
  855. second truth table (above) if the sign of ACUM1 is positive then the sign
  856. of the difference is correct.  Otherwise, the sign must be inverted.  EOR
  857. will always toggle the sign bit to the opposite state, thus automatically
  858. performing the inversion.
  859.  
  860. The last operation performed before exiting EVAL is to clear the carry flag
  861. (CLC), thus indicating that the evaluation was completed without an
  862. overflow error.
  863.  
  864. To use EVAL, the two BCD numbers to be evaluated must be deposited into the
  865. BCD accumulators.  Then, the next operation would be to actually call the
  866. EVAL subroutine and to check for a possible overflow error:
  867.  
  868.              JSR EVAL       ;EVALUATE BCD NUMBERS
  869.              BCC TOHERE     ;NO OVERFLOW ERROR
  870.                             ;
  871.              JMP ERROR      ;INDICATE ERROR TO USER
  872.                             ;
  873.     TOHERE   program continues...
  874.  
  875. Upon reaching TOHERE in the above program fragment your next step should be
  876. to transfer the result in ACUM1 back to a suitable location in memory.
  877.  
  878. VI.  RECAP
  879.  
  880. In THE ABC'S OF BCD part 3 you have learned how to transfer BCD numbers
  881. from one location in memory to another, and how to add and subtract
  882. multi-precision numbers.  You have been guide through the technique of
  883. evaluating signed BCD numbers and have been presented with a complete
  884. evaluation subroutine that utilizes the addition and subtraction techniques
  885. that have been described in chapters III and IV of this article.  You now
  886. have the ability to perform BCD math within your machine language program.
  887. In THE ABC'S OF BCD part 4 the techniques of multiplying will be presented,
  888. thus giving the tools you need to set up three of the four basic math
  889. functions.
  890.